package ar.edu.utn.frba.dds.tp.tests.adapter;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.NoSuchElementException;

import org.joda.time.LocalDate;
import org.joda.time.LocalTime;
import org.joda.time.format.DateTimeFormat;
import org.joda.time.format.DateTimeFormatter;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;

import ar.edu.utn.frba.dds.tp.code.Asientos.AsientoDeVuelo;
import ar.edu.utn.frba.dds.tp.code.Asientos.ClaseAsiento;
import ar.edu.utn.frba.dds.tp.code.Asientos.EstadoAsiento;
import ar.edu.utn.frba.dds.tp.code.Asientos.UbicacionAsiento;
import ar.edu.utn.frba.dds.tp.code.adapter.LanchitaAdapter;
import ar.edu.utn.frba.dds.tp.code.lugares.Lugar;
import ar.edu.utn.frba.dds.tp.code.usuarios.UsuarioSinCuentaPaga;
import ar.edu.utn.frba.dds.tp.code.usuarios.UsuarioStandard;
import ar.edu.utn.frba.dds.tp.code.usuarios.UsuarioVIP;
import ar.edu.utn.frba.dds.tp.code.vuelos.BuscadorVuelos;
import ar.edu.utn.frba.dds.tp.code.vuelos.Busqueda;
import ar.edu.utn.frba.dds.tp.code.vuelos.ExistingReservationException;
import ar.edu.utn.frba.dds.tp.code.vuelos.RegistroCompraAsientos;
import ar.edu.utn.frba.dds.tp.code.vuelos.Vuelo;
import com.google.common.base.Predicate;
import com.google.common.collect.Iterables;
import com.lanchita.AerolineaLanchita;
import com.lanchita.excepciones.CodigoErroneoException;

public class LanchitaTest {
	
	AerolineaLanchita aerolineaLanchita; 
	LanchitaAdapter lanchitaAdapter = new LanchitaAdapter();
	private Busqueda busqueda = new Busqueda(new Lugar("EZE"), new Lugar("USA"), new LocalDate(2012,12,20));
	
	
	@Before
	//instanciando otra Lanchita y modificando la instancia del adapter para que apunte a ésta por reflection.
	public void setUp() throws Exception {
		
		Constructor<AerolineaLanchita> cons = AerolineaLanchita.class.getDeclaredConstructor();
		cons.setAccessible(true);
	
		//devuelve nueva instancia de lanchita
		aerolineaLanchita = cons.newInstance();
	
		Field field = lanchitaAdapter.getClass().getDeclaredField("aerolineaLanchita");
	
		//agrega la nueva instancia de lanchita al adapter, para que este use la 
		//la nueva instancia y no la de posta
		field.setAccessible(true);
		field.set(lanchitaAdapter, aerolineaLanchita);
	}
	
	@Test //CUIDADO! Este test da verde porque aerolínea lanchita devuelve todos asientos de distintos vuelos.
	public void testVuelosDisponibles(){
		
		DateTimeFormatter format = DateTimeFormat.forPattern("dd/MM/yyyy");
		
		int n = aerolineaLanchita.asientosDisponibles(
				busqueda.getOrigen().getLugar(), 
				busqueda.getDestino().getLugar(), 
				busqueda.getFechaSalida().toString(format), 
				null, null,null).length;
		
		assertEquals(n, lanchitaAdapter.vuelosDisponibles(busqueda).size());
	}
	
	@Test (expected=NoSuchElementException.class)
	public void testComprarVuelo(){
		AsientoDeVuelo asiento = lanchitaAdapter.vuelosDisponibles(busqueda).get(0).getAsientos().get(0);
		lanchitaAdapter.comprarAsiento(asiento, new UsuarioSinCuentaPaga(new BigDecimal(20)));
		assertEquals(asiento.getEstado(), EstadoAsiento.COMPRADO);
		buscarEnVuelos(asiento);
		
	/*
	 * Cuando compro un asiento, el asiento se marca como comprado, entonces si le pido los vuelos disponibles a lanchitaAdapter
	 * (y sabiendo que aerolínea lanchita devuelve solo un asiento por vuelo) el vuelo que tiene ese asiento no debe estar incluído en 
	 * la respuesta. 
	 * El metodo buscarEnVuelos, al no encontrar ningún vuelo que satisfaga la condición, lanza NoSuchElementException. 
	 */
	}
	
	@Test
	public void testComprarEliminaSobreReservantes(){
		AsientoDeVuelo asiento = lanchitaAdapter.vuelosDisponibles(busqueda).get(0).getAsientos().get(0);
		UsuarioVIP reservante = new UsuarioVIP();
		asiento.agregarSobreReserva(reservante);
		assertFalse(asiento.getSobreReservas().isEmpty());
		lanchitaAdapter.comprarAsiento(asiento, reservante);
		assertTrue(asiento.getSobreReservas().isEmpty());
		assertEquals(asiento.getEstado(),EstadoAsiento.COMPRADO);
	}
	

	@Test (expected=CodigoErroneoException.class)
	public void testComprarAsientoIncorrectoTiraError(){
		
		AsientoDeVuelo asiento = new AsientoDeVuelo(20,new BigDecimal("20"),ClaseAsiento.PRIMERA,UbicacionAsiento.PASILLO,EstadoAsiento.DISPONIBLE);
		
		
		asiento.setVuelo( new Vuelo("EC0344", "EZE", "USA", new LocalDate(2012,12,20), new LocalDate(2012,12,21), new LocalTime(14, 00), new LocalTime(2, 25)));
		asiento.setNumeroDeAsiento(2);
		
		lanchitaAdapter.comprarAsiento(asiento, new UsuarioVIP());
	
		// Al enviar un numero de vuelo incorrecto, lanchita debe lanzar un CodigoErroneoException
		
	}
	
	@Test
	public void testLaListaDeVuelosNoContieneRepetidos(){
		
		
		List<Vuelo> vuelos = new ArrayList<Vuelo>();
		vuelos = lanchitaAdapter.vuelosDisponibles(busqueda);
		
		Iterator<Vuelo> iterador = vuelos.iterator();
		
		while (iterador.hasNext()) {
			Vuelo element = iterador.next();
			iterador.remove();
			for (Vuelo v: vuelos)
			assertFalse(v.getNroDeVuelo().equals(element.getNroDeVuelo()));
		}
			
		// Luego de conseguir los vuelos disponibles, me fijo que cada vuelo este 1 sola vez en la lista (que no esten repetidos)
	}
	
	@Test (expected=NoSuchElementException.class)
	public void testReservarAsiento(){
		
		AsientoDeVuelo asiento = lanchitaAdapter.vuelosDisponibles(busqueda).get(0).getAsientos().get(0);
		lanchitaAdapter.reservarAsiento(asiento, new UsuarioStandard()); // solo los usuarios standard pueden reservar
		assertEquals(asiento.getEstado(), EstadoAsiento.RESERVADO);
		buscarEnVuelos(asiento);  // al estar el asiento reservado, no se encontrara entre los disponibles que nos envia lanchita
	}
	
	@Test
	public void testAgregaSobreReservas(){
		UsuarioStandard juan = new UsuarioStandard();
		AsientoDeVuelo asiento = lanchitaAdapter.vuelosDisponibles(busqueda).get(0).getAsientos().get(0);
		lanchitaAdapter.reservarAsiento(asiento, new UsuarioStandard());
		assertTrue(asiento.getSobreReservas().size() == 0); //no hay sobre-reservas ya que el asiento estaba disponible y se pudo reservar correctamente
		try{
			lanchitaAdapter.reservarAsiento(asiento, juan);
		}
		catch(ExistingReservationException e)
		{
			asiento.agregarSobreReserva(juan);
		}
		assertTrue(asiento.getSobreReservas().contains(juan)); // el asiento contiene una sobre-reserva de juan (quiso reservar un asiento ya reservado)
	}
	
	@Test  //OJO! ESTE TEST FUNCIONA PORQUE SE CUAL ES EL PRIMER ASIENTO DISPONIBLE
	public void testTransferirReserva(){
		AsientoDeVuelo asiento = lanchitaAdapter.vuelosDisponibles(busqueda).get(0).getAsientos().get(0);
		String[] asientoString = { "01202022220202-3", "159.90", "P", "V", "D", "", "14:00","02:25","EZE","USA","20/12/2012","21/12/2012" }; // es el mismo asiento de arriba
		UsuarioStandard Juan = new UsuarioStandard();
		asiento.agregarSobreReserva(Juan); //le agrego una sobre-reserva aunque el asiento esta disponible)
		lanchitaAdapter.expiroReserva(asientoString);
		assertEquals(asiento.getEstado(), EstadoAsiento.RESERVADO); // al transferir la reserva, dicho asiento se encuentra reservado
	}
	
	@org.junit.After
	public void After(){
		BuscadorVuelos.getInstance().clearAerolineas();
	}

	private void buscarEnVuelos(final AsientoDeVuelo asiento) {
		Predicate<? super Vuelo> predicate = new Predicate<Vuelo>() {
			public boolean apply(Vuelo vuelo) {
				return vuelo.getAsientos().contains(asiento);
			}
		};
		Iterables.find(lanchitaAdapter.vuelosDisponibles(busqueda), predicate);
	}
	
	@After
	public void tearDown(){
		RegistroCompraAsientos.getInstance().clear();
	}
}
